﻿using BrightIdeasSoftware;
using Config.Net;
using netDxf;
using netDxf.Entities;
//using netDxf;
//using netDxf.Blocks;
//using netDxf.Entities;
//using netDxf.IO;
//using netDxf.Objects;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.UI.WebControls;
using System.Windows.Forms;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace Merge
{
    public partial class FrmMain : Form
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private IMergeSettings settings;
        public FrmMain()
        {
            InitializeComponent();
        }



        List<Cell> ScanFolderFiles(string folder)
        {
            List<Cell> cells = new List<Cell>();
            if (virtualD1)
            {
                foreach (string dxfFile in Directory.GetFiles(folder, "*.dxf", SearchOption.TopDirectoryOnly))
                {
                    cells.Add(new Cell(Path.GetDirectoryName(folder), folder, dxfFile));
                }
            }
            else
            {
                foreach (string d2 in Directory.GetDirectories(folder))
                {
                    ReportD3s.AddRange(Directory.GetDirectories(d2));

                    foreach (string dxfFile in Directory.GetFiles(d2, "*.dxf", SearchOption.TopDirectoryOnly))
                    {
                        cells.Add(new Cell(folder, d2, dxfFile));
                    }
                }
            }

            return cells;
        }

        private Dictionary<string, string> fullDir2ToConterDict;
        private Dictionary<string, int> fullDir2ToIndexDict;//TODO: merge
        private void SortDir2(List<Cell> cells)
        {
            fullDir2ToConterDict = new Dictionary<string, string>();
            fullDir2ToIndexDict = new Dictionary<string, int>();
            var fudirs = cells.Select(x => x.FullDir).Distinct().ToList();
            for (int i = 0; i < fudirs.Count; i++)
            {
                fullDir2ToIndexDict.Add(fudirs[i], i + settings.FolderStartIndex);
                string dirIndexString = (i + settings.FolderStartIndex).ToString().PadLeft(3, '0');
                fullDir2ToConterDict.Add(fudirs[i], dirIndexString);
                File.AppendAllLines(Constants.FolderIndexFile, new string[] { $"{dirIndexString}\t{fudirs[i]}" });
            }
            settings.FolderStartIndex += fudirs.Count;
            if (settings.FolderStartIndex > 999)
            {
                settings.FolderStartIndex = 1;
            }
            numFolderStartIndex.Value = settings.FolderStartIndex;
        }

        void AppendOutput(string s)
        {
            File.AppendAllLines(settings.OutputLogsFile, new List<string>() { s }, Encoding.UTF8);
            UpdateLableColor(lblOutputLogsFile, true);
        }



        private List<string> ReportD3s;
        private bool virtualD1 = true;



        void MergeAll()
        {


            string logFile = settings.OutputLogsFile;
            File.WriteAllText(logFile, string.Empty);
            UpdateLableColor(lblOutputLogsFile, false);
            List<Cell> cells = new List<Cell>();
            ReportD3s = new List<string>();

            foreach (string d1 in lstInputDirs.Objects.OfType<Folder>().Select(x => x.Name))
            {
                //MessageBox.Show(d1);
                if (!Directory.Exists(d1))
                {
                    log.Error($"{d1}不存在，取消扫描");
                    continue;
                }
                cells.AddRange(ScanFolderFiles(d1));
            }

            SortDir2(cells);

            foreach (Cell cell in cells)
            {
                this.iDxf.CloneDxf(cell, UpdateMsg);
            }
            cells = cells.Where(x => x.CanRead).ToList();

            foreach (Cell cell in cells)
            {
                cell.TidyFileName = tidyer.TidyName(cell.DxfFileName);
                if (cell.TidyFileName != cell.DxfFileName)
                {
                    log.Info($"规范化 {cell.DxfFileName}->{cell.TidyFileName}");
                }
            }


            var extraHighCells = cells.Where(c => c.height > settings.GridLargeHeight);
            if (extraHighCells.Any())
            {
                AppendOutput("以下超大文件，跳过：");
                foreach (Cell cell in extraHighCells)
                {
                    AppendOutput(cell.DxfFullName);
                }
            }
            bool hasD3s = ReportD3s.Any();
            if (hasD3s)
            {
                AppendOutput("以下文件夹是多余的3层文件夹：");
                foreach (string d3 in ReportD3s)
                {
                    AppendOutput(d3);
                }
                UpdateLableColor(lblOutputLogsFile, true);
            }
            this.iDxf.MergeSizeType(settings.GridNormalWidth, settings.GridNormalHeight, cells.Where(c => c.height <= settings.GridNormalHeight), settings.OutputNormalDxf, lblOutputNormalDxf,
                settings, UpdateMsg, UpdateLableColor, filters, dicNameOutOrder, fullDir2ToConterDict, fullDir2ToIndexDict);
            this.iDxf.MergeSizeType(settings.GridLargeWidth, settings.GridLargeHeight, cells.Where(c => c.height > settings.GridNormalHeight && c.height < settings.GridLargeHeight), settings.OutputLargeDxf, lblOutputLargeDxf,
                settings, UpdateMsg, UpdateLableColor, filters, dicNameOutOrder, fullDir2ToConterDict, fullDir2ToIndexDict);
            //string[] types = cells.SelectMany(x => x.ImportedBlock.Entities).Select(x => x.CodeName).Distinct().ToArray();
            //Console.WriteLine(string.Join(",", types));
        }

        private List<Folder> inputFolders;
        private void RefreshInputDirs()
        {
            this.lstInputDirs.SetObjects(inputFolders);
        }

        private void DisplaySettings()
        {
            lblOutputNormalDxf.Text = settings.OutputNormalDxf;
            toolTip1.SetToolTip(lblOutputNormalDxf, settings.OutputNormalDxf);
            lblOutputLargeDxf.Text = settings.OutputLargeDxf;
            toolTip1.SetToolTip(lblOutputLargeDxf, settings.OutputLargeDxf);
            lblOutputLogsFile.Text = settings.OutputLogsFile;
            toolTip1.SetToolTip(lblOutputLogsFile, settings.OutputLogsFile);
            numGridNormalWidth.Value = (decimal)settings.GridNormalWidth;
            numGridNormalHeight.Value = (decimal)settings.GridNormalHeight;
            numGridLargeWidth.Value = (decimal)settings.GridLargeWidth;
            numGridLargeHeight.Value = (decimal)settings.GridLargeHeight;
            numTextFolderLeftX.Value = (decimal)settings.TextFolderLeftX;
            numTextFolderHeight.Value = (decimal)settings.TextFolderHeight;
            numTextImageRightX.Value = (decimal)settings.TextImageRightX;
            numTextImageHeight.Value = (decimal)settings.TextImageHeight;
            numTextImageRows.Value = settings.TextImageRows;
            numFolderStartIndex.Value = (decimal)settings.FolderStartIndex;
            chkFilter.Checked = settings.IsFilterMode;
        }

        private Tidyer tidyer = new Tidyer(Constants.NamesTidyConfigFile);
        private FilterKeywordsManager filterKeywordsManager = new FilterKeywordsManager(Constants.FilterKeywordsFile);
        private void LoadConfig()
        {
            settings = new ConfigurationBuilder<IMergeSettings>()
             .UseIniFile(Constants.iniFile)
             .Build();
            if (settings.OnlyTestFilter)
            {
                log.Info("当前处于只分类模式。");
                this.iDxf = new NoDxf();
            }
            else
            {
                log.Info("当前处于只合并模式。");
                this.iDxf = new RealDxf(settings);
            }
            inputFolders = settings.InputDirs.Select(x => new Folder(x)).ToList();
            RefreshInputDirs();

        }
        private void Ctl_MouseWheel(object sender, MouseEventArgs e)
        {
            ((HandledMouseEventArgs)e).Handled = true;
        }
        private IDxf iDxf = null;
        private void FrmMain_Load(object sender, EventArgs e)
        {
            if (!Directory.Exists(Constants.AppDataDxfMergeDir))
                Directory.CreateDirectory(Constants.AppDataDxfMergeDir);
            foreach (Control ctl in groupBox2.Controls)
            {
                if (ctl.GetType() == typeof(NumericUpDown))
                    ctl.MouseWheel += Ctl_MouseWheel;
            }
            LoadConfig();
            DisplaySettings();
            LoadFilters();
            this.lstFilters.DragSource = new SimpleDragSource();
            this.lstFilters.DropSink = new RearrangingDropSink(true);
            if (settings.AllowFilterDrop)
            {
                this.lstInputDirs.DragSource = new SimpleDragSource();
                this.lstInputDirs.DropSink = new RearrangingDropSink(true);
            }
            else
            {
                this.lstFilters.AllowDrop = false;
            }

        }

        private bool CanWriteFile(string file)
        {
            try
            {
                using (FileStream stream = new FileInfo(Path.GetFullPath(file)).Open(FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    stream.Close();
                }
            }
            catch (IOException)
            {
                return false;
            }

            return true;
        }

        private bool CanWriteOneOutputFile(string file)
        {
            if (!File.Exists(file))
                return true;
            if (CanWriteFile(file))
            {
                return true;
            }
            UpdateMsg($"检测到{file}无法覆盖", true);
            return false;
        }

        private bool CanWriteFiles()
        {
            return CanWriteOneOutputFile(settings.OutputNormalDxf)
                && CanWriteOneOutputFile(settings.OutputLargeDxf)
                 && CanWriteOneOutputFile(settings.OutputLogsFile);
        }





        private void UpdateMsg(string s, bool append)
        {
            if (txtMsg.InvokeRequired)
            {
                txtMsg.BeginInvoke((Action)(() =>
                {
                    if (append)
                    {
                        txtMsg.AppendText(s);
                        txtMsg.AppendText(Environment.NewLine);
                    }
                    else
                        txtMsg.Text = s;
                }));
            }
            else
            {
                if (append)
                {
                    txtMsg.AppendText(s);
                    txtMsg.AppendText(Environment.NewLine);
                }
                else
                    txtMsg.Text = s;
            }
        }

        private void UpdateLableColor(LinkLabel lbl, bool has)
        {
            if (lbl.InvokeRequired)
            {
                lbl.BeginInvoke((Action)(() =>
                {
                    lbl.BackColor = has ? Color.LightCoral : Color.LightGray;
                }));
            }
            else
            {
                lbl.BackColor = has ? Color.LightCoral : Color.LightGray;
            }
        }

        private Filters filters;

        private void DisplayFilters()
        {
            chkMatchDot.Checked = settings.FuzzyMatchDot;
            lstFilters.SetObjects(filters.AllFilters.OrderBy(x => x.OutputOrder));
        }

        private void LoadFilters()
        {
            var deserializer = new DeserializerBuilder()
                .WithNamingConvention(UnderscoredNamingConvention.Instance)  // see height_in_inches in sample yml 
                .Build();
            filters = deserializer.Deserialize<Filters>(File.ReadAllText(Constants.filtersFile, Encoding.UTF8));
            for (int i = 0; i < filters.AllFilters.Length; i++)
            {
                Filter filter = filters.AllFilters[i];
                filter.SortingNumber = i + 1;
                if (filter.OptionalIncludeKeywords == null)
                {
                    filter.OptionalIncludeKeywords = new string[0];
                }
                var impliciteExcludes = filterKeywordsManager.CalcExclude(
                    filter.IncludeKeywords.Union(filter.OptionalIncludeKeywords).ToHashSet());
                filter.FinalExcludeKeywords = filter.ExcludeKeywords.Union(impliciteExcludes).ToHashSet();
                string finalExcludes = string.Join(",", filter.FinalExcludeKeywords.OrderBy(x => x));
                log.Info($"分类 {filter.Name} 匹配顺序{filter.SortingNumber} 实际排除 {finalExcludes}");
            }
            DisplayFilters();
        }

        private Dictionary<Filter, int> dicNameOutOrder = new Dictionary<Filter, int>();
        private void SetOutputOrders()
        {
            int index = 1;

            foreach (var item in lstFilters.Objects)
            {
                Filter filter = item as Filter;
                filter.OutputOrder = index++;
                if (dicNameOutOrder.ContainsKey(filter))
                {
                    dicNameOutOrder[filter] = filter.OutputOrder;
                }
                else
                {
                    dicNameOutOrder.Add(filter, filter.OutputOrder);
                }
            }
        }

        private void btnMerge_Click(object sender, EventArgs e)
        {
            SetOutputOrders();

            btnMerge.Enabled = false;
            txtMsg.Clear();
            if (CanWriteFiles())
            {

#if DEBUG
                MergeAll();
#else
                Task.Run(() =>
                {
                    try
                    {
                        MergeAll();
                    }
                    catch (Exception ex)
                    {
                        UpdateMsg(ex.Message, false);
                    }
                }).Wait();
#endif

            }
            btnMerge.Enabled = true;

        }

        private void btnAddInputDir_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.SelectedPath = settings.LastSelectedFolder;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                settings.LastSelectedFolder = dlg.SelectedPath;
                inputFolders.Add(new Folder(dlg.SelectedPath));
                RefreshInputDirs();
            }
        }

        private void btnDeleteDir_Click(object sender, EventArgs e)
        {
            foreach (Folder folder in lstInputDirs.SelectedObjects.OfType<Folder>())
            {
                inputFolders.Remove(folder);
            }
            RefreshInputDirs();
        }



        private void numGridNormalWidth_ValueChanged(object sender, EventArgs e)
        {
            settings.GridNormalWidth = (double)(sender as NumericUpDown).Value;
        }

        private void numGridNormalHeight_ValueChanged(object sender, EventArgs e)
        {
            settings.GridNormalHeight = (double)(sender as NumericUpDown).Value;
        }

        private void numGridLargeWidth_ValueChanged(object sender, EventArgs e)
        {
            settings.GridLargeWidth = (double)(sender as NumericUpDown).Value;

        }

        private void numGridLargeHeight_ValueChanged(object sender, EventArgs e)
        {
            settings.GridLargeHeight = (double)(sender as NumericUpDown).Value;

        }

        private void numTextFolderLeftX_ValueChanged(object sender, EventArgs e)
        {
            settings.TextFolderLeftX = (double)(sender as NumericUpDown).Value;

        }

        private void numTextFolderHeight_ValueChanged(object sender, EventArgs e)
        {
            settings.TextFolderHeight = (double)(sender as NumericUpDown).Value;

        }

        private void numTextImageRightX_ValueChanged(object sender, EventArgs e)
        {
            settings.TextImageRightX = (double)(sender as NumericUpDown).Value;

        }

        private void numTextImageHeight_ValueChanged(object sender, EventArgs e)
        {
            settings.TextImageHeight = (double)(sender as NumericUpDown).Value;

        }

        private void numTextImageRows_ValueChanged(object sender, EventArgs e)
        {
            settings.TextImageRows = (int)(sender as NumericUpDown).Value;

        }

        private void btnOutputNormalDxf_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "dxf|*.dxf";
            dlg.Title = "指定输出普通文件名";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                settings.OutputNormalDxf = dlg.FileName;
                lblOutputNormalDxf.Text = settings.OutputNormalDxf;
                toolTip1.SetToolTip(lblOutputNormalDxf, settings.OutputNormalDxf);
            }
        }

        private void btnOutputLargeDxf_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "dxf|*.dxf";
            dlg.Title = "指定输出超大文件名";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                settings.OutputLargeDxf = dlg.FileName;
                lblOutputLargeDxf.Text = settings.OutputLargeDxf;
                toolTip1.SetToolTip(lblOutputLargeDxf, settings.OutputLargeDxf);
            }
        }

        private void btnOutputLogsFile_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "log|*.txt;*.log";
            dlg.Title = "指定输出日志文件名";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                settings.OutputLogsFile = dlg.FileName;
                lblOutputLogsFile.Text = settings.OutputLogsFile;
                toolTip1.SetToolTip(lblOutputLogsFile, settings.OutputLogsFile);
            }
        }

        private void chkVd1_CheckedChanged(object sender, EventArgs e)
        {
            virtualD1 = (sender as System.Windows.Forms.CheckBox).Checked;
        }

        private void groupBox1_Enter(object sender, EventArgs e)
        {

        }

        private void btnCheckAll_Click(object sender, EventArgs e)
        {
            foreach (Filter filter in filters.AllFilters)
            {
                filter.Selected = true;
            }

            lstFilters.RefreshObjects(filters.AllFilters);
        }

        private void SaveFilters()
        {
            filters.AllFilters = filters.AllFilters.OrderBy(x => x.SortingNumber).ToArray();
            var serializer = new SerializerBuilder()
            .WithNamingConvention(CamelCaseNamingConvention.Instance)
            .Build();
            string yaml = serializer.Serialize(filters);
            File.WriteAllText(Constants.filtersFile, yaml, Encoding.UTF8);
        }

        private void lstFilters_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void chkFilter_CheckedChanged(object sender, EventArgs e)
        {
            settings.IsFilterMode = (sender as System.Windows.Forms.CheckBox).Checked;
        }

        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            settings.InputDirs = lstInputDirs.Objects.OfType<Folder>().Select(x => x.Name).ToArray();
            SetOutputOrders();
            SaveFilters();
        }

        private void btnReverse_Click(object sender, EventArgs e)
        {
            foreach (Filter filter in filters.AllFilters)
            {
                filter.Selected = !filter.Selected;
            }
            lstFilters.RefreshObjects(filters.AllFilters);
        }

        private void chkMatchDot_CheckedChanged(object sender, EventArgs e)
        {
            settings.FuzzyMatchDot = chkMatchDot.Checked;
        }

        private void lstFilters_SelectedIndexChanged_1(object sender, EventArgs e)
        {

        }

        private void groupBox2_Enter(object sender, EventArgs e)
        {

        }

        private void numFolderStartIndex_ValueChanged(object sender, EventArgs e)
        {
            settings.FolderStartIndex = (int)(sender as NumericUpDown).Value;
        }

        private void lblOutputNormalDxf_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            lbl_LinkClicked(sender);
        }

        private void lbl_LinkClicked(object sender)
        {
            string fn = (sender as LinkLabel).Text;
            if (File.Exists(fn))
            {
                try
                {
                    Process.Start(fn);
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message);
                }
            }
        }

        private void lblOutputLargeDxf_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            lbl_LinkClicked(sender);
        }

        private void lblOutputLogsFile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            lbl_LinkClicked(sender);
        }

        private void FrmMain_DragEnter(object sender, DragEventArgs e)
        {

        }

        private void lstInputDirs_DragEnter(object sender, DragEventArgs e)
        {

        }

        private void lstInputDirs_DragDrop(object sender, DragEventArgs e)
        {

        }

        private void btnAddInputDir_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void btnAddInputDir_DragDrop(object sender, DragEventArgs e)
        {
            string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop);

            foreach (string path in paths)
            {
                if (System.IO.Directory.Exists(path))
                {
                    //if (!lstInputDirs.Items.Contains(path))
                    //{
                    lstInputDirs.Items.Add(path);
                    //}
                    settings.LastSelectedFolder = path;
                    inputFolders.Add(new Folder(path));
                    RefreshInputDirs();
                }
            }
        }
    }
}
